package apibasej.db.transaction;

import java.util.HashMap;

import apibasej.basic.misc.standalone.TwoValues;
import apibasej.db.metadata.Col;
import apibasej.db.sql.wrapper.ColSelect;
import apibasej.db.sql.wrapper.SQLWrapperException;

public class SeqManager {

	
	
	// esta classe pode dar problema se tiver vários contextos usando o mesmo banco !!!!!!
	
	private static int sizeIncrement = 1;
	
	private static HashMap<String,Object> mapIds = new HashMap<String, Object>();
	

	// tem que ser synchron?! ??? (evitar synch ao máximo! por causa de deadloks!)
	@SuppressWarnings("unchecked")
	public static <T> T getNextSeq(String tab, String colSeq, Class<T> classColPK, GenericTransaction uc, TwoValues<String,Object>... othersFilters) throws SQLWrapperException, UtilDBException{
		StringBuilder sbKey = new StringBuilder(tab);//tab+"."+colSeq;
		sbKey.append('.');
		sbKey.append(colSeq);
		if(othersFilters!=null){
			for(TwoValues<String,Object> f : othersFilters){
				sbKey.append(';');
				sbKey.append(f.getFirst());
				sbKey.append('-');
				sbKey.append(f.getSecond());
			}
		}
		String k = sbKey.toString();
		//System.out.println("kkkkkkk>>>>"+k);
		Object lastVal = mapIds.get(k);
		//System.out.println("lastVal>>>>>"+lastVal);
		if(lastVal==null){
			UtilDBStr utilDB = new UtilDBStr(tab);
			ColSelect<?> c = utilDB.addColSelect(colSeq);
			c.setStrForColSelect("MAX("+colSeq+")");
			c.setReturnPrimitiveIfNull(true); //precisa?
			if(othersFilters!=null){
				for(TwoValues<String,Object> f : othersFilters){
					utilDB.addFilter(f.getFirst(), f.getSecond());
				}
			}
			lastVal = utilDB.execSelectUniqueValue(uc);
			//System.out.println("MAX>>>>"+utilDB.getCurrentStatementQuery().getSqlInfo());
			//if(lastVal==null) lastVal = 0;// trata depois de acordo com cada tipo
		}
		Object next = getNextVal(lastVal, k, classColPK);
		//System.out.println("next>>>>>"+next);
		mapIds.put(k, next);
		return (T)next;
	}
	
	
	
	
	@SafeVarargs
	@SuppressWarnings("unchecked")
	public static <T,V> T getNextSeq(Col<T> colSeq, GenericTransaction uc, TwoValues<Col<V>,V>... othersFilters) throws SQLWrapperException, UtilDBException{
		TwoValues<String,Object>[] othersFiltersStr = null;
		if(othersFilters!=null && othersFilters.length>0){
			othersFiltersStr = new TwoValues[othersFilters.length];
			int ct = 0;
			for(TwoValues<Col<V>,V> f : othersFilters){
				othersFiltersStr[ct++] = new TwoValues<String, Object>(f.getFirst().getName(), f.getSecond());
			}
		}
		if(othersFiltersStr==null){
			return getNextSeq(colSeq.getTab().getName(), colSeq.getName(), colSeq.getClassCol(), uc);
		}else{
			return getNextSeq(colSeq.getTab().getName(), colSeq.getName(), colSeq.getClassCol(), uc, othersFiltersStr);
		}
	}
	
	
	
	private static Object getNextVal(Object lastVal, String key, Class<?> classCol) throws SQLWrapperException, UtilDBException{
		
		//if(lastVal==null) lastVal = getSizeIncrement();
		//if(lastVal==null) throw new UtilDBException("lastVal==null ! ");// não deve ocorrer pois o MAX está com setReturnPrimitiveIfNull(true)
		
		if( Integer.class.isAssignableFrom(classCol) ) {
			if(lastVal==null) lastVal = (int)0;
			return ((Integer)lastVal)+getSizeIncrement();
		}else if( Long.class.isAssignableFrom(classCol) ) {
			if(lastVal==null) lastVal = (long)0;
			return ((Long)lastVal)+getSizeIncrement();
		}else if( Short.class.isAssignableFrom(classCol) ) {
			if(lastVal==null) lastVal = (short)0;
			return ((Short)lastVal)+getSizeIncrement();
		}else if( Byte.class.isAssignableFrom(classCol) ) {
			if(lastVal==null) lastVal = (byte)0;
			return ((Byte)lastVal)+getSizeIncrement();
		}else if( String.class.isAssignableFrom(classCol) ) {
			if(lastVal==null) lastVal = "0";
			return ""+(Integer.parseInt(""+lastVal)+getSizeIncrement());			
		}
		
		else throw new UtilDBException("Invalid type for PK column: "+key+", typeCol: "+classCol.getName()+", typeLastVal: "+lastVal.getClass().getName());
	}
	
	
	
	public static void main(String[] args) {
		System.out.println( Number.class.isAssignableFrom( Integer.class ) ); //true
		//System.out.println(Long.valueOf(l));
	}

	public static int getSizeIncrement() {
		return sizeIncrement;
	}
	public static void setSizeIncrement(int size) {
		sizeIncrement = size;
	}
	
	
}
